package com.gskj.gulimall.auth.controller;

import club.gskj.common.constant.AuthServerConstant;
import club.gskj.common.exception.BizCodeEnume;
import club.gskj.common.utils.R;
import club.gskj.common.vo.MemberRespVo;
import com.alibaba.fastjson.TypeReference;
import com.gskj.gulimall.auth.feign.MemberFeignService;
import com.gskj.gulimall.auth.feign.ThirdPartyFeignService;
import com.gskj.gulimall.auth.vo.UserLoginVo;
import com.gskj.gulimall.auth.vo.UserRegisterVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @ClassName LoginController
 * @Description TODO
 * @Author 康纳
 * @Date 2021-03-14
 */

@Controller
public class LoginController {

    @Autowired
    private ThirdPartyFeignService thirdPartyFeignService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private MemberFeignService memberFeignService;

    /***
     *  给手机号发送验证码
     * @param phone
     * @return
     */
    @GetMapping("/sms/sendCode")
    @ResponseBody
    public R sendCode(@RequestParam("phone") String phone) {

        //TODO 1.接口防刷

        // 从reids中获取code
        String redisCode = redisTemplate.opsForValue().get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + phone);
        if (!StringUtils.isEmpty(redisCode)) {
            // 2.验证码再次校验
            // 获取上次验证码创建时间 毫秒
            long l = Long.valueOf(redisCode.split("_")[1]);

            // 判断当前时间段 与之前时间段之间 ：60内是否已经发送过验证码，已发送过，则不再发送。
            if (System.currentTimeMillis() - l < 60000) {
                // 60s内不能再发送，返回错误信息
                return R.error(BizCodeEnume.VALID_SMS_CODE.getCode(), BizCodeEnume.VALID_SMS_CODE.getMsg());
            }
        }

        // redis:存key=phone,value=code; key=sms:code:手机号 - xxxxx
        String substring = UUID.randomUUID().toString().substring(0, 5);
        // 加入时间戳
        String code = substring + "_" + System.currentTimeMillis();

        // 将验证码放入缓存。设置失效时间
        // 防止同一个手机号在60s中再次发送验证码
        redisTemplate.opsForValue().set(AuthServerConstant.SMS_CODE_CACHE_PREFIX + phone, code, 1, TimeUnit.MINUTES);

        // 调用远程服务，发送验证码
        R r = thirdPartyFeignService.sendMessage(phone, substring);
        return r;
    }

    /***
     *  注册页面提交用户信息注册
     *  TODO RedirectAttributes：重定向携带数据，利用session原理，将数据放入session中，只有跳到下一个页面取出数据后，session中的数据就会删除
     *  TODO 但是分布式情况下 session会有问题，
     */
    @PostMapping("/userRegister")
    public String userRegister(@Valid UserRegisterVo userVo, BindingResult result, Model model, RedirectAttributes attributes) {
        // 判断页面传值的 JSR303是否有数据校验异常
        if (result.hasErrors()) {
            // 封装错误信息
            // TODO jsr303 一个字段多个校验注解同时满足时，封装Map会抛出异常 重复key异常
            Map<String, String> collect = result.getFieldErrors().stream().collect(Collectors.toMap(fieldError -> {
                return fieldError.getField();
            }, fieldError -> {
                return fieldError.getDefaultMessage();
            }));
            //model.addAttribute("errors", collect);

            //有校验错误， 转发到 注册页面
            // 低级错误： Request method 'POST' not supported，
            // 分析： 从页面表单发送post请求到controller，发送数据校验异常，再转发到wenMVC路径映射 /register.html. 出现Request method 'POST' not supported，
            // 默认 路径映射请求方式必须是 Get请求，而当前forward: 为转发Post请求方式，故报错。
            //return "/register";

            // 重定向到页面，防止表单页面重复提交数据，并使用 RedirectAttributes携带数据到 指定页面显示
            attributes.addFlashAttribute("errors", collect);
            return "redirect:http://auth.gulimall.com/register.html";
        }

        // 注册用户信息
        // TODO 调用远程服务
        // 表单验证码
        String code = userVo.getCode();
        // 表单手机号
        String phone = userVo.getPhone();

        // 获取缓存中存的验证码
        String redisCode = redisTemplate.opsForValue().get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + phone);
        // 判断是否为空
        if (StringUtils.isEmpty(redisCode)) {
            // 放入错误信息
            Map<String, String> errors = new HashMap<>();
            errors.put("code", "验证码失效，请重新获取");
            attributes.addFlashAttribute("errors", errors);
            // 重定向到 注册页面
            return "redirect:http://auth.gulimall.com/register.html";
        }

        // 验证码不为空，校验表单验证码
        if (!code.equalsIgnoreCase(redisCode.split("_")[0])) {
            // 验证码验证不通过
            // 放入错误信息
            Map<String, String> errors = new HashMap<>();
            errors.put("code", "验证码验证错误，请检查验证码是否正确");
            attributes.addFlashAttribute("errors", errors);
            // 重定向到 注册页面
            return "redirect:http://auth.gulimall.com/register.html";
        }

        // 验证通过
        // 删除验证码
        redisTemplate.delete(AuthServerConstant.SMS_CODE_CACHE_PREFIX + phone);
        // 调用远程服务，
        R r = memberFeignService.registerMember(userVo);
        if (r.getCode() == 0) {
            // 保存成功
            // 重定向到 登陆页面
            return "redirect:http://auth.gulimall.com/login.html";
        } else {
            Map<String, String> errors = new HashMap<>();
            errors.put("msg", r.getData("msg", new TypeReference<String>() {
            }));
            attributes.addFlashAttribute("errors", errors);
            return "redirect:http://auth.gulimall.com/register.html";
        }
    }


    /**
     * 跳转到 登陆页面
     *
     * @return
     */
    @GetMapping("/login.html")
    public String loginPage(HttpSession session) {
        // 判断当前用户是否已存在
        Object attribute = session.getAttribute(AuthServerConstant.SESSION_LOGIN_USER);
        if (attribute == null) {
            return "login";
        }
        return "redirect:http://gulimall.com";
    }


    /***
     *
     * @param vo 表单数据
     * @return java.lang.String
     * @author 康纳-connor
     * @creed: Talk is cheap,show me the code
     * @date 21.3.16 21:36
     * @Description 前台用户登陆
     * */
    @PostMapping("/userLogin")
    public String userLogin(UserLoginVo vo, RedirectAttributes attributes, HttpSession session) {

        // 调用远程服务登陆
        R r = memberFeignService.memberLogin(vo);
        if (r.getCode() == 0) {
            // 登陆成功，返回首页
            MemberRespVo data = r.getData("data", new TypeReference<MemberRespVo>() {
            });
            // 存储到session
            session.setAttribute(AuthServerConstant.SESSION_LOGIN_USER, data);
            return "redirect:http://gulimall.com";
        }
        Map<String, Object> errors = new HashMap<>(3);
        // 存放错误信息
        errors.put("msg", r.getData("msg", new TypeReference<String>() {
        }));
        attributes.addFlashAttribute("errors", errors);
        // 登陆失败，返回登陆页
        return "redirect:http://auth.gulimall.com/login.html";
    }


}

